package org.deltaset.meta.internal;

import java.lang.reflect.Method;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.deltaset.meta.EntityResolver;
import org.deltaset.meta.ExecuteListener;
import org.deltaset.meta.ExecutorResult;
import org.deltaset.meta.Finder;
import org.deltaset.meta.MetaAction;
import org.deltaset.meta.MetaActionExecutor;
import org.deltaset.meta.MetaActionHandler;
import org.deltaset.meta.TitleResolver;
import org.deltaset.meta.extension.RepositoryProvider;

/**
 *
 * @author dmakariev
 */
class MetaActionHandlerImpl implements MetaActionHandler {

    private static final Logger logger = Logger.getLogger(MetaActionHandlerImpl.class.getName());
    private final RepositoryProvider repositoryProvider;
    private final EntityResolver entityResolver;
    private final TitleResolver titleResolver;
    private final ExecuteListener executeListener;

    MetaActionHandlerImpl(
            final RepositoryProvider repositoryProvider,
            final TitleResolver titleResolver,
            final ExecuteListener executeListener) {    
        this.repositoryProvider = repositoryProvider;
        this.entityResolver = repositoryProvider.createEntityResolver();
        this.titleResolver = titleResolver;
        this.executeListener = executeListener;
    }

    @Override
    public Object loadModel(final Class entityClass,final MetaAction findByIdAction, final String id) {
        if (null != id && !"".equals(id.trim())) {
            //final Class entityClass = metaClass.getEntityClass();
            final Object entityId = entityResolver.convertToEntityId(id, entityClass);
            //final MetaAction findByIdAction = metaClass.getFindByIdAction();
            return findById(findByIdAction, entityId);
        }
        logger.log(Level.SEVERE, "ERROR loadModel(id) : id cannot be null !");
        throw new RuntimeException("ERROR loadModel(id) : id cannot be null !");
    }

    private Object findById(final MetaAction findByIdAction, final Object entityId) {
        //final MetaAction findByIdAction = metaClass.getFindByIdAction();
        return executeAction(findByIdAction, null, new Object[]{entityId});
    }

    @Override
    public Finder newPageHelper(final MetaActionExecutor finderExecutor, final MetaAction defaultFinder) {
        if (null == finderExecutor) {
            // final MetaAction metaFinder = metaClass.getDefaultFinder();
            final Object[] parameters = new Object[0];
            return this.newPageHelper(defaultFinder, parameters);
        } else {
            final MetaAction metaFinder = finderExecutor.getMetaAction();
            final Object[] parameters = finderExecutor.getParameters();
            return this.newPageHelper(metaFinder, parameters);
        }
    }

    private Finder newPageHelper(final MetaAction metaFinder, final Object[] parameters) {

        final Class executeClass = metaFinder.getExecuteClass();

        final Class entityClass = metaFinder.getEntityClass();
        //final Object repository = RepositoryRegistry.lookUp(executeClass, entityClass);
        final Object repository = repositoryProvider.locateRepository(executeClass, entityClass);

        //final PageHelper all = PageHelper.newInstance(new DelegatingFinder(metaFinder, repository, parameters)).load(pageNum);
        final Finder finder = new DelegatingFinder(metaFinder, repository, parameters);
        return finder;
    }

    @Override
    public boolean isNew(Object model) {
        if (null == model) {
            return true;
        }
        return null == getModelId(model);
    }

    @Override
    public String getModelId(final Object model) {
        if (null == model) {
            return null;
        }
        final Object extractId = entityResolver.extractEntityId(model);
        return (null == extractId) ? null : String.valueOf(extractId);
    }

    //XXX: static
    @Override
    public ExecutorResult executeAction(final MetaActionExecutor executor, final Object model) {
        final MetaAction metaAction = executor.getMetaAction();
        final Object[] parameterValues = executor.getParameters();
        final Object resultValue = executeAction(metaAction, model, parameterValues);
        final ExecutorResultImpl executorResult = new ExecutorResultImpl(resultValue, metaAction, repositoryProvider);
        return executorResult;
    }

    //XXX: static
    @Override
    public Object executeAction(final MetaAction metaAction, final Object model, final Object[] parameterValues) {

        //this call is intended only for auditing purposes 
        executeListener.auditExecuteAction(metaAction, model, parameterValues);
        logger.log(Level.INFO, "MetaCrudHandler executeAction {0}", metaAction);
        final Class entityClass = metaAction.getEntityClass();
        final Class executeClass = metaAction.getExecuteClass();
        final Method method = metaAction.getMethod();
        logger.log(Level.INFO, "   entityClass={0}", entityClass);
        logger.log(Level.INFO, "   executeClass={0}", executeClass);
        logger.log(Level.INFO, "   metaAction={0}", metaAction);

        if (metaAction.isContext()) {
            if (metaAction.isEntityMember()) {
                if (0 == parameterValues.length) {
                    logger.log(Level.INFO, "   1 service={0}", model.getClass().getName());
                    final Object actionResult = ReflectionUtils.execute(method, model);
                    return actionResult;
                } else {
                    logger.log(Level.INFO, "   2 service={0}", model.getClass().getName());
                    final Object actionResult = ReflectionUtils.execute(method, model, parameterValues);
                    return actionResult;
                }
            } else {
                if (0 == parameterValues.length) {
                    //final Object service = RepositoryRegistry.lookUp(executeClass, entityClass);
                    final Object service = repositoryProvider.locateRepository(executeClass, entityClass);
                    logger.log(Level.INFO, "   3 service={0}", service.getClass().getName());
                    final Object actionResult = ReflectionUtils.execute(method, service, model);
                    return actionResult;
                } else {
                    //final Object service = RepositoryRegistry.lookUp(executeClass, entityClass);
                    final Object service = repositoryProvider.locateRepository(executeClass, entityClass);
                    logger.log(Level.INFO, "   4 service={0}", service.getClass().getName());
                    final Object actionResult = ReflectionUtils.execute(method, service, model, parameterValues);
                    return actionResult;
                }
            }
        }
        if (0 == parameterValues.length) {
            logger.log(Level.INFO, "   5 executeClass={0}", executeClass);
            logger.log(Level.INFO, "   5 entityClass={0}", entityClass);
            //final Object service = RepositoryRegistry.lookUp(executeClass, entityClass);
            final Object service = repositoryProvider.locateRepository(executeClass, entityClass);
            logger.log(Level.INFO, "   5 service={0};method={1}", new Object[]{service.getClass().getName(), method.getName()});
            final Object actionResult = ReflectionUtils.execute(method, service);
            logger.log(Level.INFO, "   actionResult={0}", actionResult);
            return actionResult;
        } else {
            //final Object service = RepositoryRegistry.lookUp(executeClass, entityClass);
            final Object service = repositoryProvider.locateRepository(executeClass, entityClass);
            logger.log(Level.INFO, "   6 service={0}", service.getClass().getName());
            final Object actionResult = ReflectionUtils.execute(method, service, parameterValues);
            return actionResult;
        }
    }

    //TODO: think!!.. shoudl go to executor.. eventually.. 
    //here the model parameter should be an entityClass ?.. 
    //XXX: static
    @Override
    public boolean isDisabled(final MetaAction metaAction, final Object model) {
        final Method disableMethod = metaAction.getDisableMethod();
        final boolean isDisabled = executeContextBooleanMethod(disableMethod, metaAction, model);
        return isDisabled;
    }

    //XXX: static
    @Override
    public boolean isHidden(final MetaAction metaAction, final Object model) {

        if (true == executeListener.hideAction(metaAction, model)) {
            return true;
        }

        final Method hideMethod = metaAction.getHideMethod();
        final boolean isHidden = executeContextBooleanMethod(hideMethod, metaAction, model);
        return isHidden;
    }

    private boolean executeContextBooleanMethod(final Method booleanMethod, final MetaAction metaAction, final Object model) {

        if (null == metaAction || null == model || null == booleanMethod) {
            return false;
        }
        final boolean isContext = metaAction.isContext();
        final Class entityClass = metaAction.getEntityClass();
        final boolean isModelAnEntityClass = entityClass.equals(model.getClass());
        if (false == isModelAnEntityClass) {
            return false;
        }
        if (false == isContext) {
            //this is for factory and finder methods
            return false;
        }

        final boolean isEntityMember = metaAction.isEntityMember();
        if (true == isEntityMember) {
            final boolean booleanResult = (Boolean) ReflectionUtils.execute(booleanMethod, model);
            return booleanResult;
        } else {
            final Class executeClass = metaAction.getExecuteClass();
            //final Object service = RepositoryRegistry.lookUp(executeClass, entityClass);
            final Object service = repositoryProvider.locateRepository(executeClass, entityClass);
            final boolean booleanResult = (Boolean) ReflectionUtils.execute(booleanMethod, service, model);
            return booleanResult;
        }
    }

    @Override
    public String modelTitle(Object model) {
        return titleResolver.modelTitle(model);
    }
}